编写一个简单的发布器(publisher)和订阅器(subscriber)
发布器
mkdir -p ~/catkin_ws/src/beginner_tutorials/src
先在beginner_tutorials下创建一个src。 -p的意思是如果路径中有不存在的文件夹,则创建- 在新建的文件夹里面新建cpp文件,talker.cpp,将一下内容复制进去。
#include "ros/ros.h"
#include "std_msgs/String.h"
#include <sstream>
int main(int argc, char **argv)
{
ros::init(argc, argv, "talker");
ros::NodeHandle n;
ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000);
ros::Rate loop_rate(10);
int count = 0;
while (ros::ok())
{
std_msgs::String msg;
std::stringstream ss;
ss << "hello world " << count;
msg.data = ss.str();
ROS_INFO("%s", msg.data.c_str());
chatter_pub.publish(msg);
ros::spinOnce();
loop_rate.sleep();
++count;
}
return 0;
}
#include “ros/ros.h”
是最常用的使用ros系统必须包含进去的头文件#include “std_msgs/String.h”
另一个头文件,由String.msg自动生成。ros::init(argc, argv, “talker”);
在使用ROS 系统之前都必须先初始化。参数有三个,前两个必须是argc argv,第三个是节点名字。ros::init()可以通过命令行进行名字的重映射。ros::NodeHandle n;
创建一个talker的句柄,创建的第一个还会初始化节点,最后一个销毁的会清除node使用的资源(这里还没见过有创建多个的,不知道这里的销毁是不是多个节点的最后一个。ros::Publisher chatter_pub = n.advertise(“chatter”, 1000);
告诉master我们要在chatter话题上发布std_msgs/String的消息,然后master会告诉所有正在收听cahtter的节点我们要发布消息了。第二个参数是我们发布队列的大小,如果我们发消息太快,超过了1000条信息,缓冲区会把之前的消息扔掉。 NodeHandle::advertise()返回一个ros::Publisher对象,有一个成员函数publish()可以让我们在topic上发布消息,如果类型不对,拒绝发布(不太懂)ros::Rate loop_rate(10);
ros::Rate对象允许我们指定循环的频率。记录距离上一次Rate::sleep()调用的时间,然后休眠正确的时间,比如这里是10Hz一次循环。int count = 0; while (ros::ok()) {
计算发布消息的次数,按ctrl+c将ros::ok()置为false 还有下面情况ros::ok()也会为false -被另一同名节点踢出ROS网络 -ros::shutdown()被程序的另一部分调用 -所有的ros::NodeHandles都已经被销毁
chatter_pub.publish(msg);
发布消息ROS_INFO(“%s”, msg.data.c_str());
就是printf在ROS中的代替ros::spinOnce();
使得回调函数(callback)能够被调用,加上最好loop_rate.sleep()
通过sleep休眠来使得发布频率为10hz
订阅器
现在在beginner_tutorials package下创建src/listener.cpp。粘贴代码:
#include "ros/ros.h"
#include "std_msgs/String.h"
void chatterCallback(const std_msgs::String::ConstPtr& msg)
{
ROS_INFO("I heard: [%s]", msg->data.c_str());
}
int main(int argc, char **argv)
{
ros::init(argc, argv, "listener");
ros::NodeHandle n;
ros::Subscriber sub = n.subscribe("chatter", 1000, chatterCallback);
ros::spin();
return 0;
}
只说与发布器不同的部分
void chatterCallback(const std_msgs::String::ConstPtr& msg) { ROS_INFO("I heard: [%s]", msg->data.c_str()); }
回调函数,消息到达chatter topic时会被调用,消息是以boost shared_ptr指针的形式传输,我们可以存储它而不需要复制数据
ros::Subscriber sub = n.subscribe("chatter", 1000, chatterCallback);
这是告诉master我们要订阅chatter上的消息,有消息到达topic时,ROS会调用chatterCallback()的函数。第二个参数是队列的大小,同publicer的参数。返回ros::Subscriber。ros::spin();
ros::spin()进入自循环,尽可能快的调用消息回调函数。如果没有消息到达,不会占用很多CPU,一旦ros::ok()返回FALSE,ros::spin()会立刻跳出自循环。
编译节点
将下列代码加到CMakeLilst.txt的尾
include_directories(include ${catkin_INCLUDE_DIRS})
add_executable(talker src/talker.cpp)
target_link_libraries(talker ${catkin_LIBRARIES})
add_dependencies(talker beginner_tutorials_generate_messages_cpp)
add_executable(listener src/listener.cpp)
target_link_libraries(listener ${catkin_LIBRARIES})
add_dependencies(listener beginner_tutorials_generate_messages_cpp)
这样可以加入两个可执行文件,talker和listener,这两个会自动在devel下生成,路径是~/catkin_ws/devel/lib/share/ add_dependencies(talker beginner_tutorials_generate_messages_cpp)添加对生成的消息文件的依赖 运行catkin_make
测试
- 运行roscore
运行talker
rosrun beginner_tutorials talker
运行订阅器
rosrun beginner_tutorials listener
可以看到两个终端分别是发布消息和订阅消息(hello world和heard hello world)